/*
 * Copyright (c) 2011 AndroidPlot.com. All rights reserved.
 *
 * Redistribution and use of source without modification and derived binaries with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY ANDROIDPLOT.COM ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANDROIDPLOT.COM OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of AndroidPlot.com.
 */

package com.androidplot.xy;

import android.util.Pair;
import com.androidplot.series.XYSeries;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;


/**
 * A convenience class used to create instances of XYPlot generated from Lists of Numbers.
 * Note that SimpleXYSeries is not synchronized; never alter the underlying model of a SimpleXYSeries during a call to Plot.redraw() on a Plot with which
 * the SimpleXYSeries instance has been registered.  It is the developer's responsibility to implement the synchronization
 * mechanism(s) to accomplish this.
 */
public class SimpleXYSeries implements XYSeries {

    public enum ArrayFormat {
        Y_VALS_ONLY,
        XY_VALS_INTERLEAVED
    }

    private LinkedList<Number> xVals;
    private LinkedList<Number> yVals = null;

    private String title = null;

    {
        xVals = new LinkedList<Number>();
        yVals = new LinkedList<Number>();
    }

    public SimpleXYSeries(String title) {
        this.title = title;
    }
    /**
     * Generates an XYSeries instance from the List of numbers passed in.  This is a convenience class
     * and should only be used for static data models; it is not suitable for representing dynamically
     * changing data.
     *
     * @param model  A List of Number elements comprising the data model.
     * @param format Format of the model.  A format of Y_VALS_ONLY means that the array only contains y-values.
     *               For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model.
     * @param title  Title of the series
     */
    public SimpleXYSeries(List<? extends Number> model, ArrayFormat format, String title) {
        this(title);
        setModel(model, format);
    }

    public SimpleXYSeries(List<? extends Number> xVals, List<? extends Number> yVals, String title) {
        this(title);
        if(xVals == null || yVals == null) {
            throw new IllegalArgumentException("Neither the xVals nor the yVals parameters may be null.");
        }

        if(xVals.size() != yVals.size()) {
            throw new IllegalArgumentException("xVals and yVals List parameters must be of the same size.");
        }

        this.xVals.addAll(xVals);
        this.yVals.addAll(yVals);
    }

    /**
     *
     * @param model A List of Number elements comprising the data model.
     * @param format Format of the model.  A format of Y_VALS_ONLY means that the array only contains y-values.
     *               For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model.
     */
    public void setModel(List<? extends Number> model, ArrayFormat format) {

        // empty the current values:
        xVals.clear();
        yVals.clear();

        // make sure the new model has data:
        if(model == null || model.size() == 0) {
            return;
        }

        switch (format) {

            // array containing only y-vals. assume x = index:
            case Y_VALS_ONLY:
                for (int i = 0; i < model.size(); i++) {
                    xVals.add(i);
                    yVals.add(model.get(i));
                }
                break;

            // xy interleaved array:
            case XY_VALS_INTERLEAVED:
                if (model.size() % 2 != 0) {
                    throw new IndexOutOfBoundsException("Cannot auto-generate series from odd-sized xy List.");
                }
                // always need an x and y array so init them now:
                int sz = model.size()/2;
                for (int i = 0, j = 0; i < sz; i++, j += 2) {
                    xVals.add(model.get(j));
                    yVals.add(model.get(j+1));
                }
                break;
            default:
                throw new IllegalArgumentException("Unexpected enum value: " + format);
        }
    }

    /**
     * Sets individual x value based on index
     * @param value
     * @param index
     */
    public void setX(Number value, int index) {
        if(xVals == null || xVals.size() <= index) {
            throw new IndexOutOfBoundsException();
        }
        xVals.set(index, value);
    }

    /**
     * Sets individual y value based on index
     * @param value
     * @param index
     */
    public void setY(Number value, int index) {
        if(yVals == null || yVals.size() <= index) {
            throw new IndexOutOfBoundsException();
        }
        yVals.set(index, value);
    }

    public synchronized void addFirst(Number x, Number y) {
        xVals.addFirst(x);
        yVals.addFirst(y);
    }

    /**
     *
     * @return Pair<Number, Number> with first equal to x-val and second equal to y-val.
     */
    public synchronized Pair<Number, Number> removeFirst() {
        if(size() <= 0) {
            throw new NoSuchElementException();
        }
        return new Pair<Number, Number>(xVals.removeFirst(), yVals.removeFirst());
    }

    public synchronized void addLast(Number x, Number y) {
        xVals.addLast(x);
        yVals.addLast(y);
    }

    /**
     *
     * @return Pair<Number, Number> with first equal to x-val and second equal to y-val.
     */
    public synchronized Pair<Number, Number> removeLast() {
        if(size() <= 0) {
            throw new NoSuchElementException();
        }
        return new Pair<Number, Number>(xVals.removeLast(), yVals.removeLast());
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public int size() {
        if(xVals != null) {
            return xVals.size();
        } else {
            return 0;
        }
    }

    @Override
    public Number getX(int index) {
        return xVals.get(index);
    }

    @Override
    public Number getY(int index) {
        return yVals.get(index);
    }
}
